package com.hqd.ch03.v26.utils;

import com.hqd.ch03.v26.jdbc.datasource.ConnectionHolder;
import com.hqd.ch03.v26.tx.transaction.TransactionDefinition;
import com.hqd.ch03.v26.tx.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public abstract class DataSourceUtils {
    public static Connection getConnection(DataSource dataSource) throws SQLException {
        return doGetConnection(dataSource);
    }

    private static Connection doGetConnection(DataSource dataSource) throws SQLException {
        Object resource = TransactionSynchronizationManager.getResource(dataSource);
        if (resource instanceof ConnectionHolder) {
            return ((ConnectionHolder) resource).getConnection();
        }
        return null;
    }


    public static void releaseConnection(Connection con, DataSource dataSource) {
        try {
            doReleaseConnection(con, dataSource);
        } catch (SQLException ex) {
            ex.printStackTrace();
        } catch (Throwable ex) {
            ex.printStackTrace();
        }
    }

    public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {
        if (con == null) {
            return;
        }
        if (dataSource != null) {
            ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
            if (conHolder != null && connectionEquals(conHolder, con)) {
                conHolder.released();
                return;
            }
        }
        doCloseConnection(con, dataSource);
    }

    private static boolean connectionEquals(ConnectionHolder conHolder, Connection passedInCon) {
        if (!conHolder.hasConnection()) {
            return false;
        }
        Connection heldCon = conHolder.getConnection();
        return (heldCon == passedInCon || heldCon.equals(passedInCon));
    }

    public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {
        con.close();
    }

    public static Integer prepareConnectionForTransaction(Connection con, TransactionDefinition definition)
            throws SQLException {
        if (definition != null && definition.isReadOnly()) {
            try {
                con.setReadOnly(true);
            } catch (SQLException | RuntimeException ex) {
                Throwable exToCheck = ex;
                while (exToCheck != null) {
                    if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
                        throw ex;
                    }
                    exToCheck = exToCheck.getCause();
                }
            }
        }

        Integer previousIsolationLevel = null;
        if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
            int currentIsolation = con.getTransactionIsolation();
            if (currentIsolation != definition.getIsolationLevel()) {
                previousIsolationLevel = currentIsolation;
                con.setTransactionIsolation(definition.getIsolationLevel());
            }
        }

        return previousIsolationLevel;
    }

    public static void applyTransactionTimeout(Statement stmt, DataSource dataSource) throws SQLException {
        applyTimeout(stmt, dataSource, -1);
    }

    public static void applyTimeout(Statement stmt, DataSource dataSource, int timeout) throws SQLException {
        ConnectionHolder holder = null;
        if (dataSource != null) {
            holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        }
        /**
         * 这里不得不吐槽下，超时起了个这么个名称，实际上对crud都生效
         */
        if (holder != null && holder.hasTimeout()) {
            stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());
        } else if (timeout >= 0) {
            stmt.setQueryTimeout(timeout);
        }
    }
}
